/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "CreatePrimary_fp.h"

#if CC_CreatePrimary  // Conditional expansion of this file

/*(See part 3 specification)
// Creates a primary or temporary object from a primary seed.
*/
//  Return Type: TPM_RC
//      TPM_RC_ATTRIBUTES       sensitiveDataOrigin is CLEAR when sensitive.data is an 
//                              Empty Buffer 'fixedTPM', 'fixedParent', or 
//                              'encryptedDuplication' attributes are inconsistent 
//                              between themselves or with those of the parent object;
//                              inconsistent 'restricted', 'decrypt' and 'sign'
//                              attributes
//                              attempt to inject sensitive data for an asymmetric 
//                              key;
//      TPM_RC_KDF              incorrect KDF specified for decrypting keyed hash 
//                              object
//      TPM_RC_KEY              a provided symmetric key value is not allowed
//      TPM_RC_OBJECT_MEMORY    there is no free slot for the object
//      TPM_RC_SCHEME           inconsistent attributes 'decrypt', 'sign', 
//                              'restricted' and key's scheme ID; or hash algorithm is
//                              inconsistent with the scheme ID for keyed hash object
//      TPM_RC_SIZE             size of public authorization policy or sensitive
//                              authorization value does not match digest size of the
//                              name algorithm; or sensitive data size for the keyed
//                              hash object is larger than is allowed for the scheme
//      TPM_RC_SYMMETRIC        a storage key with no symmetric algorithm specified; 
//                              or non-storage key with symmetric algorithm different 
//                              from TPM_ALG_NULL
//      TPM_RC_TYPE             unknown object type
/*

tpm2_createprimary(1) - This command is used to create a primary object under one of the hierarchies: Owner, Platform, Endorsement, NULL. 
The command will create and load a Primary Object. The sensitive and public portions are not returned. A context file for the created 
object's handle is saved as a file for future interactions with the created primary.

tpm2_createprimary(1)-此命令用于在一个层次结构下创建主对象: Owner、 Platform、 Endorense、 NULL。该命令将创建并加载一个主对象。
敏感部分和公共部分不会返回。用于创建的对象句柄的上下文文件保存为一个文件，以便将来与创建的主要对象进行交互。

This command is used to create a Primary Object under one of the Primary Seeds or a Temporary Object under TPM_RH_NULL. 
The command uses a TPM2B_PUBLIC as a template for the object to be created.
The size of the unique field shall not be checked for consistency with the other object parameters. The command will create 
and load a Primary Object. The sensitive area is not returned.
此命令用于在主种子之一下创建主对象或在 TPM_RH_NULL 下创建临时对象。 该命令使用 TPM2B_PUBLIC 作为要创建的对象的模板。
不应检查唯一字段的大小是否与其他对象参数一致。 该命令将创建并加载一个主对象。 敏感区域不返回。

NOTE 1 Since the sensitive data is not returned, the key cannot be reloaded. It can either be made persistent or it can be recreated.
NOTE 2 For interoperability, the unique field should not be set to a value that is larger than allowed by object parameters, so that the unmarshaling will not fail.
NOTE 3 An Empty Buffer is a legal unique field value.
EXAMPLE 1 A TPM_ALG_RSA object with a keyBits of 2048 in the objects parameters should have a unique field that is no larger than 256 bytes.
EXAMPLE 2 A TPM_ALG_KEYEDHASH or a TPM_ALG_SYMCIPHER object should have a unique field this is no larger than the digest produced by the object’s nameAlg
注 1 由于未返回敏感数据，因此无法重新加载密钥。 它既可以持久化，也可以重新创建。
注 2 为了互操作性，唯一字段不应设置为大于对象参数所允许的值，以便解组不会失败。
注 3 空缓冲区是合法的唯一字段值。
示例 1 对象参数中 keyBits 为 2048 的 TPM_ALG_RSA 对象应具有不大于 256 字节的唯一字段。
示例 2 TPM_ALG_KEYEDHASH 或 TPM_ALG_SYMCIPHER 对象应具有唯一字段，该字段不大于对象的 nameAlg 生成的摘要

Any type of object and attributes combination that is allowed by TPM2_Create() may be created by this command. 
The constraints on templates and parameters are the same as TPM2_Create() except that a Primary Storage Key and 
a Temporary Storage Key are not constrained to use the algorithms of their parents.
For setting of the attributes of the created object, fixedParent, fixedTPM, decrypt, and restricted are implied 
to be SET in the parent (a Permanent Handle). The remaining attributes are implied to be CLEAR.
The TPM will derive the object from the Primary Seed indicated in primaryHandle using an approved KDF. All of the 
bits of the template are used in the creation of the Primary Key. Methods for creating a Primary Object from a 
Primary Seed are described in TPM 2.0 Part 1 and implemented in TPM 2.0 Part 4.
If this command is called multiple times with the same inPublic parameter, inSensitive.data, and Primary Seed, the
TPM shall produce the same Primary Object.
TPM2_Create() 允许的任何类型的对象和属性组合都可以通过此命令创建。 模板和参数的约束与 TPM2_Create() 相同，除了主存储密钥和临时存储密钥不受限于使用其父代的算法。
对于已创建对象的属性设置，fixedParent、fixedTPM、decrypt 和 restricted 隐含在父对象（永久句柄）中设置。 其余属性隐含为 CLEAR。
TPM 将使用批准的 KDF 从 primaryHandle 中指示的主要种子派生对象。 模板的所有位都用于创建主键。 从主种子创建主对象的方法在 TPM 2.0 第 1 部分中描述并在 TPM 2.0 第 4 部分中实现。
如果使用相同的 inPublic 参数、inSensitive.data 和 Primary Seed 多次调用此命令，则 TPM 应生成相同的 Primary Object。

NOTE 4 If the Primary Seed is changed, the Primary Objects generated with the new seed shall be statistically 
unique even if the parameters of the call are the same.
This command requires authorization. Authorization for a Primary Object attached to the Platform Primary Seed 
(PPS) shall be provided by platformAuth or platformPolicy. Authorization for a Primary Object attached to the 
Storage Primary Seed (SPS) shall be provided by ownerAuth or ownerPolicy.
Authorization for a Primary Key attached to the Endorsement Primary Seed (EPS) shall be provided by endorsementAuth
or endorsementPolicy.
注 4 如果主种子发生变化，即使调用的参数相同，用新种子生成的主对象在统计上也是唯一的。
此命令需要授权。 附加到平台主要种子 (PPS) 的主要对象的授权应由 platformAuth 或 platformPolicy 提供。
附加到存储主种子 (SPS) 的主对象的授权应由 ownerAuth 或 ownerPolicy 提供。
附加到背书主种子 (EPS) 的主密钥的授权应由 endorsementAuth 或 endorsementPolicy 提供。


*/
TPM_RC
TPM2_CreatePrimary(
    CreatePrimary_In    *in,            // IN: input parameter list
    CreatePrimary_Out   *out            // OUT: output parameter list
    )
{
    TPM_RC               result = TPM_RC_SUCCESS;
    TPMT_PUBLIC         *publicArea;
    DRBG_STATE           rand;
    OBJECT              *newObject;
    TPM2B_NAME           name;

// Input Validation
    // Will need a place to put the result
    newObject = FindEmptyObjectSlot(&out->objectHandle);
    if(newObject == NULL)
        return TPM_RC_OBJECT_MEMORY;
    // Get the address of the public area in the new object 
    // (this is just to save typing)
    publicArea = &newObject->publicArea;

    *publicArea = in->inPublic.publicArea;

    // Check attributes in input public area. CreateChecks() checks the things that
    // are unique to creation and then validates the attributes and values that are
    // common to create and load.
    result = CreateChecks(NULL, publicArea, 
                          in->inSensitive.sensitive.data.t.size);
    if(result != TPM_RC_SUCCESS)
        return RcSafeAddToResult(result, RC_CreatePrimary_inPublic);
    // Validate the sensitive area values
    if(!AdjustAuthSize(&in->inSensitive.sensitive.userAuth,
                       publicArea->nameAlg))
        return TPM_RCS_SIZE + RC_CreatePrimary_inSensitive;
// Command output
    // Compute the name using out->name as a scratch area (this is not the value
    // that ultimately will be returned, then instantiate the state that will be
    // used as a random number generator during the object creation.
    // The caller does not know the seed values so the actual name does not have
    // to be over the input, it can be over the unmarshaled structure.
    result = DRBG_InstantiateSeeded(&rand, 
                           &HierarchyGetPrimarySeed(in->primaryHandle)->b, 
                           PRIMARY_OBJECT_CREATION,
                           (TPM2B *)PublicMarshalAndComputeName(publicArea, &name),
                           &in->inSensitive.sensitive.data.b);
    if(result == TPM_RC_SUCCESS)
    {
        newObject->attributes.primary = SET;
        if(in->primaryHandle == TPM_RH_ENDORSEMENT)
            newObject->attributes.epsHierarchy = SET;

        // Create the primary object.
        result = CryptCreateObject(newObject, &in->inSensitive.sensitive,
            (RAND_STATE *)&rand);
    }
    if(result != TPM_RC_SUCCESS)
        return result;

    // Set the publicArea and name from the computed values
    out->outPublic.publicArea = newObject->publicArea;
    out->name = newObject->name;

    // Fill in creation data
    FillInCreationData(in->primaryHandle, publicArea->nameAlg,
                       &in->creationPCR, &in->outsideInfo, &out->creationData,
                       &out->creationHash);

    // Compute creation ticket
    TicketComputeCreation(EntityGetHierarchy(in->primaryHandle), &out->name,
                          &out->creationHash, &out->creationTicket);

    // Set the remaining attributes for a loaded object
    ObjectSetLoadedAttributes(newObject, in->primaryHandle);
    return result;
}

#endif // CC_CreatePrimary